######################################################################
package Swignition::GenericParser::GRDDL;
######################################################################

# $Id: GRDDL.pm 162 2009-03-13 17:08:02Z mail@tobyinkster.co.uk $

use Clone qw(clone);
use CSS::RDFEASE qw(:standard);
use Swignition::Misc;
use Swignition::GenericParser::RDF;
use Swignition::GenericParser::RDFa;
use strict;

sub consume
{
	my $this = shift;
	my @transformations;

	push @transformations, transformations_rel($this);
	push @transformations, transformations_attr($this);
	push @transformations, transformations_profile($this);
	push @transformations, transformations_namespace($this);
	
	my $i = 1;
	
	foreach my $t (@transformations)
	{
		my $uri = URI->new($t->{uri});
		next if ($this->{NoFollow}->{ $uri->host });
	
		my $request  = HTTP::Request->new(GET => $t->{uri});
		$request->header('Accept', 'application/xslt+xml, '
		                         . 'text/xslt, '
		                         . 'text/xsl, '
		                         . 'text/x-rdf+css, '
		                         . 'text/css, '
					 . $Swignition::Misc::acceptHeader);
		# Include the URI of the page that's being transformed.
		$request->header('Referer', $this->uri);
		# Include the URI of the namespace/profile that sent us here.
		$request->header('X-Referer-Via', $t->{via})
			if length $t->{via};
		my $response = $this->get_ua->request($request);
		
		if ($response->header('content-type') =~ m#xslt?#i
		||  $response->content =~ m#http://www.w3.org/1999/XSL/Transform#)
		{
			my $parser     = XML::LibXML->new();
			my $xslt       = XML::LibXSLT->new();
			my $style_doc  = $parser->parse_string($response->content);

			my $stylesheet;
			eval { $stylesheet = $xslt->parse_stylesheet($style_doc); };
			
			unless ($@)
			{
				my $results    = $stylesheet->transform($this->{DOM});
				my $rdfxml     = $stylesheet->output_string($results);
			
				if ($stylesheet->media_type eq 'application/rdf+xml')
				{
					Swignition::RdfXmlParser::redland_parse(
						$this,
						'rdfxml',
						$rdfxml,
						$this->graph,
						$this->graph(sprintf('GRDDL%02d', $i++)));
				}
				
				# Need to put some other media types in here.
				
				# Default to RDF/XML
				else
				{
					Swignition::RdfXmlParser::redland_parse(
						$this,
						'rdfxml',
						$rdfxml,
						$this->graph,
						$this->graph(sprintf('GRDDL%02d', $i++)));
				}
			}
		}
		
		elsif ($response->header('content-type') =~ /text\/(css|x\-rdf\+css)/i)
		{
			# Get RDFa output of RDF-EASE as a XML::LibXML DOM structure.
			my $domOutput = rdfease_to_rdfa($response->content,
				$this->{DOM}->documentElement->toString,
				1);
			
			# Temporarily switch DOMs.
			my $tmp = $this->{DOM};
			$this->{DOM} = $domOutput;
			
			# Consume RDFa
			Swignition::GenericParser::RDFa::consume(
				$this,
				undef,
				undef,
				undef,
				undef,
				undef,
				undef,
				undef,
				sprintf('GRDDL%02d', $i++));
			
			# Restore original DOM to its rightful place.
			$this->{DOM} = $tmp;
		}
		
		else
		{
			# What to do?!
		}
	}
}

sub is_enabled
{
	my $this = shift;
	return ($this->{DOM} && $this->{'options'}->{'p_grddl'});
}

# TODO - check with spec: are we supposed to do this on every element?
sub transformations_attr
{
	my $this = shift;
	my @transformations;

	my $attr = $this->{DOM}->documentElement->getAttributeNS(
		'http://www.w3.org/2003/g/data-view#', 'transformation');
	my @t = split /\s+/, $attr;
	foreach my $t (@t)
	{
		next unless ($t =~ /[a-z0-9\.]/i);
		push @transformations, {'uri'=>$this->uri($t)};
	}
	
	return @transformations;
}

sub transformations_namespace
{
	my $this = shift;
	my @transformations;

	# We skip namespaces for (X)HTML. The XHTML namespace only contains an RDFa
	# transformation, and we have native support for RDFa anyway.
	if ($this->{options}->{grddl_fetch}
	&&  ('Swignition::HtmlParser' ne ref $this))
	{
		# Here is the namespace URI
		my $nsuri    = $this->uri( $this->{DOM}->documentElement->namespaceURI );

		# NoFollow option
		my $uri = URI->new($nsuri);
		return @transformations
			if ($this->{NoFollow}->{ $uri->host });
		
		# Request the URI
		my $request  = HTTP::Request->new(GET => "$nsuri");
		$request->header('Referer', $this->uri);
		my $response = Swignition::Misc::get_ua->request($request);

		# Set options for a recursive parse.
		my $opts          = clone($this->{options});
		$opts->{ua}       = $this->get_ua;
		$opts->{request}  = $request;
		$opts->{response} = $response;
		$opts->{base}     = "$uri";
		$opts->{p_rdfx}--;

		# Recursive parse.
		my $external_page = Swignition::GenericParser::new_by_type($response->content, $opts);
		
		# If MIME type was unrecognised by new_by_type, then try as RDF/XML.
		$external_page = Swignition::RdfXmlParser->new($response->content, $opts)
			if (!$external_page);

		# Need to parse the namespace document and find a triple
		# <nsuri> grddl:namespaceTransformation <transformuri> .
		if ($external_page)
		{
			my @vals;
			
			# @@TODO: this won't work anymore!
			@vals = @{ $external_page->{'RDF'}->{"$nsuri"}->{'properties'}
				->{'http://www.w3.org/2003/g/data-view#namespaceTransformation'}
				->{'values'} }
				if ($external_page->{RDF}->{"$nsuri"});
			
			foreach my $val (@vals)
			{
				push @transformations, 
					{
						'uri' => $val->toString,
						'via' => $nsuri,
					}
					if ($val->type);
			}
		}
	}
	
	return @transformations;
}

sub transformations_profile
{
	my $this = shift;
	my @transformations;

	# If this feature is enabled, and the parser is of the HTML flavour, then
	# get a list of transformations defined in the profiles.
	if ($this->{options}->{grddl_fetch}
	&& ('Swignition::HtmlParser' eq ref $this))
	{
		PROFILE: foreach my $p (keys %{$this->{Profiles}})
		{
			my $uri = URI->new($p);
			next if ($this->{NoFollow}->{ $uri->host });
		
			# Request the URI
			my $request  = HTTP::Request->new(GET => $p);
			$request->header('Referer', $this->uri);
			my $response = Swignition::Misc::get_ua->request($request);

			# Set options for a recursive parse.
			my $opts          = clone($this->{options});
			$opts->{ua}       = $this->get_ua;
			$opts->{request}  = $request;
			$opts->{response} = $response;
			$opts->{base}     = "$uri";
			$opts->{p_rdfx}   = 0; # Don't do a recursive parse.

			# Recursive parse.
			$this->{Profiles}->{$p} = Swignition::GenericParser::new_by_type($response->content, $opts);
		
			# If MIME type was unrecognised by new_by_type, then try as HTML.
			$this->{Profiles}->{$p} = Swignition::HtmlParser->new($response->content, $opts)
				if (! $this->{Profiles}->{$p});

			# Need to parse the namespace document and find a triple
			# <profileuri> grddl:profileTransformation <transformuri> .
			if ((ref $this->{Profiles}->{$p}) =~ /^Swignition\:\:(.+)Parser/)
			{
				my @vals;
				
				# @@TODO: this won't work anymore!
				@vals = @{ $this->{Profiles}->{$p}->{'RDF'}->{$p}->{'properties'}
					->{'http://www.w3.org/2003/g/data-view#profileTransformation'}
					->{'values'} }
					if ($this->{Profiles}->{$p}->{RDF}->{$p});
				
				foreach my $val (@vals)
				{
					push @transformations, 
						{
							'uri' => $val->toString,
							'via' => $p,
						}
						if ($val->type);
				}
			}
		}
	}
	
	return @transformations;
}

sub transformations_rel
{
	my $this = shift;
	my @transformations;
	
	# Get a list of locally defined transformations.
	if ($this->{Profiles}->{'http://www.w3.org/2003/g/data-view'}
		|| !$this->{options}->{grddl_strict_profiles} )
	{
		my $subject = $this->graph->lookup_node($this->uri, 1);
		my @triples = $subject->query_resource('http://www.w3.org/2003/g/data-view#transformation');
		foreach my $t (@triples)
		{
			my $v = $t->object;
		
			next unless $v->uri;
			my $uri = URI->new($v->uri);
			next if ($this->{NoFollow}->{ $uri->host });

			push @transformations, {'uri'=>$v->uri};
		}
	}
	
	return @transformations;
}

1;
